﻿using Microsoft.Extensions.Caching.Distributed;
using System.Collections.Concurrent;

namespace Zhp.Cache
{
    public class MemoryCacheService : ICacheService
    {
        private readonly IDistributedCache _cache;

        public MemoryCacheService(IDistributedCache cache)
        {
            _cache = cache;
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        #region 基本操作

        public T Get<T>(string key)
        {
            var bytes = _cache.Get(key);
            return bytes.BytesToObject<T>();
        }

        public async Task<T> GetAsync<T>(string key, CancellationToken token = default)
        {
            var bytes = await _cache.GetAsync(key, token);
            return bytes.BytesToObject<T>();
        }

        public void Set<T>(string key, T value, TimeSpan? expire = null)
        {
            var exp = new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire };
            _cache.Set(key, value.ObjectToBytes(), exp);
        }

        public async Task SetAsync<T>(string key, T value, TimeSpan? expire = null, CancellationToken token = default)
        {
            var exp = new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire };
            await _cache.SetAsync(key, value.ObjectToBytes(), exp, token);
        }

        public void Refresh(string key)
        {
            _cache.Refresh(key);
        }

        public async Task RefreshAsync(string key, CancellationToken token = default)
        {
            await _cache.RefreshAsync(key);
        }

        public void Remove(string key)
        {
            _cache.Remove(key);
        }

        public async Task RemoveAsync(string key, CancellationToken token = default)
        {
            await _cache.RemoveAsync(key, token);
        }

        public void Remove(string[] keys)
        {
            foreach (var key in keys)
            {
                _cache.Remove(key);
            }
        }

        public async Task RemoveAsync(string[] keys, CancellationToken token = default)
        {
            foreach (var key in keys)
            {
                await _cache.RemoveAsync(key, token);
            }
        }

        #endregion

        #region 集合操作

        private static readonly object lockObject = new object();

        public List<T> ListRange<T>(string key)
        {
            return Get<List<T>>(key);
        }

        public void ListRightPush<T>(string key, T value)
        {
            lock (lockObject)
            {
                var list = Get<List<T>>(key);
                if (list == null)
                {
                    list = new List<T>();
                }
                list.Add(value);
                _cache.Set(key, list.ObjectToBytes());
            }
        }

        public void ListRightPushArray<T>(string key, T[] values)
        {
            lock (lockObject)
            {
                var list = Get<List<T>>(key);
                if (list == null)
                {
                    list = new List<T>();
                }
                list.AddRange(values);
                _cache.Set(key, list.ObjectToBytes());
            }
        }

        public void ListRemove<T>(string key, T value)
        {
            lock (lockObject)
            {
                var list = Get<List<T>>(key);
                if (list != null && list.Any())
                {
                    list.RemoveAll(x => x.ToJson() == value.ToJson());
                    if (list.Any())
                    {
                        _cache.Set(key, list.ObjectToBytes());
                    }
                    else
                    {
                        _cache.Remove(key);
                    }
                }
            }
        }

        public T ListLeftPop<T>(string key)
        {
            lock (lockObject)
            {
                var list = Get<List<T>>(key);
                if (list != null && list.Any())
                {
                    var value = list.FirstOrDefault();
                    list.Remove(value);
                    if (list.Any())
                    {
                        _cache.Set(key, list.ObjectToBytes());
                    }
                    else
                    {
                        _cache.Remove(key);
                    }
                    return value;
                }
                return default(T);
            }
        }

        #endregion

        #region 哈希操作

        private static ConcurrentDictionary<string, ConcurrentDictionary<string, string>> _hash = new ConcurrentDictionary<string, ConcurrentDictionary<string, string>>();
        public T HashObjGet<T>(string key, string field)
        {
            if (_hash.ContainsKey(key))
            {
                return _hash[key][field].ToJson().ToObject<T>();
            }
            else
            {
                return default(T);
            }
        }

        public T HashObjGet<T>(string key)
        {
            if (_hash.ContainsKey(key))
            {
                return (_hash[key]).ConDictToObject<T>();
            }
            else
            {
                return default(T);
            }
        }

        public void HashObjSet<T>(string key, T value)
        {
            _hash[key] = value.ObjectToConDict();
        }

        public void HashObjSet(string key, string hashField, object value)
        {
            if (!_hash.ContainsKey(key))
            {
                _hash[key] = new ConcurrentDictionary<string, string>();
            }

            _hash[key][hashField] = value.ToJson();
        }

        public void HashObjDelete(string key)
        {
            _hash.TryRemove(key, out _);
        }


        #endregion

        #region 哈希集合操作

        public T HashListGet<T>(string key, object fieldId)
        {
            if (_hash.TryGetValue(key, out var dictValue))
            {
                if (dictValue.TryGetValue(fieldId.ToString(), out var value))
                {
                    return value.ToString().ToObject<T>();
                }
            }
            return default(T);
        }

        public List<T> HashListGetAll<T>(string key)
        {
            var list = new List<T>();
            if (_hash.TryGetValue(key, out var dictValue))
            {
                if (dictValue.Values.Any())
                {
                    foreach (var item in dictValue.Values)
                    {
                        list.Add(item.ToObject<T>());
                    }
                }
            }
            return list;
        }

        public void HashListSet<T>(string key, Func<T, object> fieldFunc, T value)
        {
            if (!_hash.ContainsKey(key))
            {
                _hash[key] = new ConcurrentDictionary<string, string>();
            }

            _hash[key][fieldFunc(value).ToString()] = value.ToJson();
        }

        public void HashListSet<T>(string key, Func<T, object> fieldFunc, List<T> values)
        {
            if (!_hash.ContainsKey(key))
            {
                _hash[key] = new ConcurrentDictionary<string, string>();
            }

            values.ForEach(x => _hash[key][fieldFunc(x).ToString()] = x.ToJson());
        }


        public void HashListDelete(string key, string field)
        {
            if (_hash.ContainsKey(key))
            {
                _hash[key].Remove(field, out string value);
            }
        }
        #endregion
    }
}
